﻿// ----------------------------------------------------------------------------------
// Sample Code from Fernando Machado Píriz's blog: http://fernandomachadopiriz.com
//
// <copyright company="Fernando Machado Píriz" file="MainView.xaml.cs">
// Copyright (c) Fernando Machado Píriz. Distributed under FSF's GPL v2.</copyright>
// ----------------------------------------------------------------------------------
// This code and information are provided "as is" without warranty of any kind,
// either expressed of implied, including but not limited to the implied warranties
// of merchantability and/or fitness for a particular purpose.
// ----------------------------------------------------------------------------------
namespace GivingAPresentation
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Reflection;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Shell;
    using GivingAPresentation.Commons;
    using Drawing = System.Drawing;

    /// <summary>
    /// Interaction logic for MainView.xaml
    /// </summary>
    public partial class MainView : Window
    {
        /// <summary>
        /// The NotifyHelper instance to show an icon in the notification area of the taskbar when the main windows is minimized during
        /// presentations.
        /// </summary>
        private NotifyHelper notifyHelper;

        /// <summary>
        /// Initializes a new instance of the MainView class. Compose parts exporting <code>IContract</code> contract.
        /// </summary>
        public MainView()
        {
            IList<Exception> exceptions = new List<Exception>();

            try
            {
                InitializeComponent();

                this.ViewModel = this.Resources["viewModel"] as MainViewModel;
                this.Model = this.ViewModel.Model;
                this.Model.PropertyChanged += new PropertyChangedEventHandler(this.Model_PropertyChanged);

                this.CreateNotifyHelper();
                this.FillAboutTab();
            }
            catch (Exception e)
            {
                exceptions.Add(e);
            }

            ExceptionHelper.ShowExceptions(exceptions);
        }

        /// <summary>
        /// Gets the view model of this view in the Model View View Model pattern. The instance is automatically created by XAML code and the
        /// field is assigned in the constructor.
        /// </summary>
        public MainViewModel ViewModel { get; private set; }

        /// <summary>
        /// Gets the model of this view in the Model View View Model pattern. The instance is created by the view model and asigned in the
        /// constructor.
        /// </summary>
        public MainModel Model { get; private set; }

        /// <summary>
        /// Adds a command to the jump list. When giving a presentation, the command enables stopping the presentation without opening the
        /// application. Command are implemented by lauching the application again with "stop" and "start" arguments respectively.
        /// </summary>
        /// <param name="givingAPresentation">A value indicating whether the user is giving a presentation.</param>
        private void AddJumpListTask(bool givingAPresentation)
        {
            try
            {
                string path = Assembly.GetEntryAssembly().Location;

                JumpList jl = new JumpList();

                jl.JumpItems.Clear();
                JumpTask task;

                if (givingAPresentation)
                {
                    task = new JumpTask();
                    task.ApplicationPath = path;
                    task.Title = "Stop giving a presentation";
                    task.IconResourcePath = path;
                    task.IconResourceIndex = 1;
                    task.Arguments = "stop";
                }
                else
                {
                    task = new JumpTask();
                    task.ApplicationPath = path;
                    task.Title = "Start giving a presentation";
                    task.IconResourcePath = path;
                    task.IconResourceIndex = 2;
                    task.Arguments = "start";
                }

                jl.JumpItems.Add(task);
                jl.Apply();
            }
            catch
            {
            }
        }

        /// <summary>
        /// Removes commands from the jump list.
        /// </summary>
        private void RemoveJumpListTask()
        {
            try
            {
                JumpList jl = new JumpList();
                jl.JumpItems.Clear();
                jl.Apply();
            }
            catch
            {
            }
        }

        /// <summary>
        /// Creates a notify icon using a NotifyHelper instance.
        /// </summary>
        private void CreateNotifyHelper()
        {
            this.notifyHelper = new NotifyHelper(this);
            this.notifyHelper.BalloonTipText = "Currently not giving a presentation.";
            this.notifyHelper.BalloonTipTitle = "Giving a Presentation";
            Assembly assembly = Assembly.GetExecutingAssembly();
            Stream stream = assembly.GetManifestResourceStream("GivingAPresentation.Resources.Presentation.ico");
            if (stream != null) 
            {
                Drawing.Icon icon = new Drawing.Icon(stream);
                this.notifyHelper.Icon = icon;
            }
        }

        /// <summary>
        /// Fills the About tab with information extracted from application's assembly properties.
        /// </summary>
        private void FillAboutTab()
        {
            FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(
                Process.GetCurrentProcess().MainModule.FileName);
            textBlockAbout.Text = String.Format(
                "{0}\n{1}\n{2}",
                fileVersionInfo.FileDescription,
                fileVersionInfo.LegalCopyright,
                fileVersionInfo.FileVersion);
        }
        
        /// <summary>
        /// Creates a new tab for the given extension, with a scroll viewer inside the tab, with a stack panel inside the scroll viewer. Returns
        /// the stack panel just created.
        /// </summary>
        /// <param name="extension">The extension to add to the tab control.</param>
        /// <returns>The stack panel added.</returns>
        private StackPanel CreateNewStackPanelIntoNewTab(Lazy<IContract, IMetadata> extension)
        {
            StackPanel newStackPanel;
            newStackPanel = new StackPanel();
            newStackPanel.Width = float.NaN;
            newStackPanel.Height = float.NaN;
            newStackPanel.VerticalAlignment = VerticalAlignment.Stretch;
            newStackPanel.HorizontalAlignment = HorizontalAlignment.Stretch;

            ScrollViewer scrollViewer = new ScrollViewer();
            scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
            scrollViewer.HorizontalAlignment = HorizontalAlignment.Stretch;
            scrollViewer.VerticalAlignment = VerticalAlignment.Stretch;
            scrollViewer.Content = newStackPanel;

            TabItem newTab = new TabItem();
            newTab.Header = extension.Metadata.Tab;
            newTab.Content = scrollViewer;
            tabControl.Items.Insert(tabControl.Items.Count - 1, newTab);

            return newStackPanel;
        }

        /// <summary>
        /// Fired when window is loaded. Inserts controls provided by parts into the appropriate tab in the main window. Only the defaut
        /// tab is already created; additional tabs are created if needed.
        /// </summary>
        /// <param name="sender">The parameter is not used.</param>
        /// <param name="e">The parameter is not used.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            FrameworkElement element;
            StackPanel stackPanel;
            ScrollViewer scrollViewer;

            this.AddJumpListTask(false);
            
            foreach (Lazy<IContract, IMetadata> extension in this.ViewModel.Parts)
            {
                // Control does not specifies a tab; add it to the default tab.
                if (String.IsNullOrEmpty(extension.Metadata.Tab))
                {
                    stackPanel = stackPanelDefault;
                }
                else
                {
                    // Look for the stack panel inside the tab specified by the control.
                    stackPanel = null;
                    foreach (TabItem tabItem in tabControl.Items)
                    {
                        if (tabItem.Header.ToString().Equals(extension.Metadata.Tab))
                        {
                            scrollViewer = tabItem.Content as ScrollViewer;
                            stackPanel = scrollViewer.Content as StackPanel;
                        }
                    }

                    // If the tab specified by the control was not found create a new one with a stack panel inside
                    if (stackPanel == null)
                    {
                        stackPanel = this.CreateNewStackPanelIntoNewTab(extension);
                    }
                }

                element = extension.Value.Element;
                stackPanel.Children.Add(element);
                element.HorizontalAlignment = HorizontalAlignment.Stretch;
            }

            this.ViewModel.InitializeParts.Execute(null);
        }

        /// <summary>
        /// Fired when the windows is closed. Saves parts' settingsManager and removes parts.
        /// </summary>
        /// <param name="sender">The parameter is not used.</param>
        /// <param name="e">The parameter is not used.</param>
        private void Window_Closed(object sender, EventArgs e)
        {
            FrameworkElement element;

            this.ViewModel.FinalizeParts.Execute(null);

            foreach (Lazy<IContract, IMetadata> extension in this.ViewModel.Parts)
            {
                element = extension.Value.Element;

                ////TODO: Review this... not all parts are in teh default stack panel!
                stackPanelDefault.Children.Remove(element);
            }
        }

        /// <summary>
        /// Fired when the windows is about to close. If the user is giving a presentation, ask the user to stop doing the presentation before
        /// close. This way all settings changes made when presentation started are reverted back.
        /// </summary>
        /// <param name="sender">The parameter is not used.</param>
        /// <param name="e">The parameter is not used.</param>
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (this.Model.Presenting)
            {
                if (MessageBox.Show(
                    "Cannot close while you are giving a presentation. Do you want to stop giving a presentation and then close?", 
                    "Giving a presentation", 
                    MessageBoxButton.YesNo, 
                    MessageBoxImage.Question) == MessageBoxResult.Yes)
                {
                    this.Model.Presenting = false;
                    e.Cancel = false;
                }
                else
                {
                    e.Cancel = true;
                }
            }
        }

        /// <summary>
        /// Fired when presentation starts or ends. Changes notify icon balloon tip to reflect status. Minimizes the window when presentation
        /// starts and restores the window when presentation ends.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">A <code>System.ComponentModel.PropertyChangedEventArgs</code> that contains the event data.</param>
        private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (sender.Equals(this.Model) && e.PropertyName.Equals("Presenting"))
            {
                if (this.Model.Presenting)
                {
                    this.AddJumpListTask(true);
                    this.notifyHelper.BalloonTipText = "Currently giving a presentation. Configured settings are now in effect.";
                    WindowState = WindowState.Minimized;
                }
                else
                {
                    this.AddJumpListTask(false);
                    this.notifyHelper.BalloonTipText = "Currently not giving a presentation.";
                    WindowState = WindowState.Normal;
                }
            }
        }
    }
}
